/******************************
 * 公司：......
 * 作者：shelman
 * 日期：2023年04月04日
 * 版本：V1.0
 * 摘要：无锁环形队列缓存区（非本人原创），参考其他博主文章，路径为：https://blog.csdn.net/HandsomeHong/article/details/129645815。
 * ****************************/

#pragma once


#include <stddef.h>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <thread>
#include <vector>
#include <array>

template <typename T, size_t N>
class LockFreeRingQueue
{
public:
    LockFreeRingQueue() : read_idx_(0), write_idx_(0), data_{} {}

    bool push(const T& item, bool block = false) { return pushImpl(item, block); }
    bool push(T&& item, bool block = false) { return pushImpl(std::move(item), block); }

    bool pop(T& item, bool block = false)
    {
        // 只有一个读者，read_idx_的读取可以不加锁
        size_t current_read_idx = read_idx_.load(std::memory_order_relaxed);

        // 保证读到write_idx_的变化，此处memory_order_acquire发挥的是可见性问题
        while (current_read_idx == write_idx_.load(std::memory_order_acquire))
        {
            if (!block)
            {
                return false;
            }

            std::this_thread::yield(); // 让出时间片，效率较高
        }

        // 必须先把数据读走read_idx_才能+1，memory_order_release保证了对item的写不会被重排到read_idx_ + 1之后
        item = std::move(data_[current_read_idx]);
        read_idx_.store(next(current_read_idx), std::memory_order_release);

        return true;
    }

    template <typename Func>
    bool pop(Func&& func, bool block = false)
    {
        size_t current_read_idx = read_idx_.load(std::memory_order_relaxed);

        while (current_read_idx == write_idx_.load(std::memory_order_acquire))
        {
            if (!block)
            {
                return false;
            }

            std::this_thread::yield(); // 让出时间片，效率较高
        }

        T item = std::move(data_[current_read_idx]);
        read_idx_.store(next(current_read_idx), std::memory_order_release);

        func(std::move(item));

        return true;
    }

    void popAsync(const T& value, std::function<void(bool)> callback)
    {
        auto task = [this, value, callback]()
        {
            bool result = pop(value, true);
            callback(result);
        };

        std::thread(std::move(task)).detach();
        return;
    }

    bool isEmpty() const {
        return read_idx_.load(std::memory_order_acquire) ==
                write_idx_.load(std::memory_order_acquire);
    }

    bool isFull() const {
        return next(write_idx_.load(std::memory_order_acquire)) ==
                read_idx_.load(std::memory_order_acquire);
    }

private:
    template <typename Item>
    bool pushImpl(Item&& item, bool block = false)
    {
        // 只有1个写线程, 所以write_idx_可以不加锁
        size_t current_write_idx = write_idx_.load(std::memory_order_relaxed);
        size_t next_write_idx = next(current_write_idx);

        // 读线程会修改read_idx_, 所以此处需要保证看到read_idx_的变化,此处memory_order_acquire保证的是可见性问题
        while (next_write_idx == read_idx_.load(std::memory_order_acquire))
        {
            if(!block)
            {
                return false;
            }

            std::this_thread::yield(); // 让出时间片，效率较高
        }

        // 数据的写入必须在write_idx_+1之前
        data_[current_write_idx] = std::forward<Item>(item);

        // 保证之前的写操作对读线程可见，即读线程能立刻看到data_刚写入的数据，当然也包括write_idx_的+1变化, memory_order_release会保证对data_的写入在write_idx_+1的操作之前完成。
        // 因为就算data_的赋值语句放在write_idx_+1之前，由于编译器或者运行期指令重排，并不一定能保证data_赋值语句就一定在write_idx_+1前执行。
        write_idx_.store(next_write_idx, std::memory_order_release);

        return true;
    }

    size_t next(size_t current_idx) const { return (current_idx + 1) % (N + 1); } // 此处笔者做了修改，N改成N+1

    std::atomic<size_t> read_idx_;
    std::atomic<size_t> write_idx_;
    std::array<T, (N + 1)> data_;  // +1，是因为, 循环队列如果不接入size变量，仅靠读写指针会损失一个位置
};
